﻿using System;
using System.Collections.Generic;

namespace MingYuanYun.Configuration
{
    // https://github.com/aspnet/Configuration/blob/9ca263557d6340cd69c0d5153629ff10b349cb28/src/Config.CommandLine/CommandLineConfigurationProvider.cs
    internal class CommandLineParser
    {
        private readonly Dictionary<string, string> _switchMappings;

        public CommandLineParser(IEnumerable<string> args)
        {
            if (args == null)
            {
                throw new ArgumentNullException(nameof(args));
            }

            _switchMappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            Parse(args);
        }

        public string GetCommandLineVariable(string arg)
        {
            _switchMappings.TryGetValue(arg, out string value);

            return value ?? string.Empty;
        }

        private void Parse(IEnumerable<string> args)
        {
            string key, value;

            using (var enumerator = args.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    var currentArg = enumerator.Current ?? string.Empty;
                    var keyStartIndex = 0;

                    if (currentArg.StartsWith("--"))
                    {
                        keyStartIndex = 2;
                    }
                    else if (currentArg.StartsWith("-"))
                    {
                        keyStartIndex = 1;
                    }
                    else if (currentArg.StartsWith("/"))
                    {
                        // "/SomeSwitch" is equivalent to "--SomeSwitch" when interpreting switch mappings
                        // So we do a conversion to simplify later processing
                        currentArg = $"--{currentArg.Substring(1)}";
                        keyStartIndex = 2;
                    }

                    var separator = currentArg.IndexOf('=');

                    if (separator < 0)
                    {
                        if (keyStartIndex == 0)
                        {
                            throw new FormatException($"Unrecognized argument format: '{currentArg}'.");
                        }

                        // If the switch is a key in given switch mappings, interpret it
                        if (_switchMappings.ContainsKey(currentArg))
                        {
                            key = _switchMappings[currentArg];
                        }
                        // If the switch starts with a single "-" and it isn't in given mappings , it is an invalid usage
                        else if (keyStartIndex == 1)
                        {
                            throw new FormatException($"The short switch '{currentArg}' is not defined in the switch mappings.");
                        }
                        // Otherwise, use the switch name directly as a key
                        else
                        {
                            key = currentArg.Substring(keyStartIndex);
                        }

                        if (!enumerator.MoveNext())
                        {
                            throw new FormatException($"Value for switch '{currentArg}' is missing.");
                        }

                        value = enumerator.Current;
                    }
                    else
                    {
                        var keySegment = currentArg.Substring(0, separator);

                        // If the switch is a key in given switch mappings, interpret it
                        if (_switchMappings.ContainsKey(keySegment))
                        {
                            key = _switchMappings[keySegment];
                        }
                        // If the switch starts with a single "-" and it isn't in given mappings , it is an invalid usage
                        else if (keyStartIndex == 1)
                        {
                            throw new FormatException($"The short switch '{currentArg}' is not defined in the switch mappings.");
                        }
                        // Otherwise, use the switch name directly as a key
                        else
                        {
                            key = currentArg.Substring(keyStartIndex, separator - keyStartIndex);
                        }

                        value = currentArg.Substring(separator + 1);
                    }

                    // Override value when key is duplicated. So we always have the last argument win.
                    _switchMappings[key] = value;
                }
            }
        }
    }
}
